/**
 * The contents of this file are subject to the OpenMRS Public License
 * Version 1.0 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://license.openmrs.org
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations
 * under the License.
 *
 * Copyright (C) OpenMRS, LLC.  All Rights Reserved.
 */
package org.openmrs.module.datalogging.db.hibernate;

import java.util.List;
import java.util.Map;
import java.util.Set;

import org.hibernate.Criteria;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.openmrs.BaseOpenmrsObject;
import org.openmrs.Encounter;
import org.openmrs.Obs;
import org.openmrs.Person;
import org.openmrs.module.datalogging.configuration.Event;
import org.openmrs.module.datalogging.configuration.Variable;
import org.openmrs.module.datalogging.db.DataLoggingDAO;
import org.openmrs.module.datalogging.enums.EventType;
import org.openmrs.module.datalogging.model.LogEvent;

/**
 * Hibernate operation from the summary module
 */
public class HibernateDataLoggingDAO implements DataLoggingDAO {
	
	private SessionFactory sessionFactory;
	
	/**
	 * @param sessionFactory
	 */
	public void setSessionFactory(SessionFactory sessionFactory) {
		this.sessionFactory = sessionFactory;
	}
	
	/**
	 * @see org.openmrs.module.datalogging.db.DataLoggingDAO#saveLogEvent(org.openmrs.module.datalogging.model.LogEvent)
	 */
	@Override
	public LogEvent saveLogEvent(LogEvent logEvent) {
		sessionFactory.getCurrentSession().saveOrUpdate(logEvent);
		return logEvent;
	}
	
	/**
	 * @see org.openmrs.module.datalogging.db.DataLoggingDAO#getEvent(java.lang.Integer)
	 */
	@Override
	public LogEvent getLogEvent(Integer eventId) {
		return (LogEvent) sessionFactory.getCurrentSession().get(LogEvent.class, eventId);
	}
	
	/**
	 * @see org.openmrs.module.datalogging.db.DataLoggingDAO#getLogEvents(org.openmrs.Person)
	 */
	@SuppressWarnings("unchecked")
	@Override
	public List<LogEvent> getLogEvents(Person person) {
		Criteria criteria = sessionFactory.getCurrentSession().createCriteria(LogEvent.class);
		criteria.add(Restrictions.eq("person", person));
		
		criteria.add(Restrictions.eq("voided", false));
		criteria.addOrder(Order.desc("dateCreated"));
		criteria.addOrder(Order.desc("dateChanged"));
		return criteria.list();
	}
	
	/**
	 * @see org.openmrs.module.datalogging.db.DataLoggingDAO#getAllEvents()
	 */
	@SuppressWarnings("unchecked")
    @Override
	public List<LogEvent> getLogEvents() {
		Criteria criteria = sessionFactory.getCurrentSession().createCriteria(LogEvent.class);
		
		criteria.add(Restrictions.eq("voided", false));
		criteria.addOrder(Order.desc("dateCreated"));
		criteria.addOrder(Order.desc("dateChanged"));
		return criteria.list();
	}
	
	/**
	 * @see org.openmrs.module.datalogging.db.DataLoggingDAO#saveEvent(org.openmrs.module.datalogging.configuration.Event)
	 */
	@Override
	public Event saveEvent(Event event) {
		sessionFactory.getCurrentSession().saveOrUpdate(event);
		return event;
	}
	
	/**
	 * @see org.openmrs.module.datalogging.db.DataLoggingDAO#getEvent(java.lang.Integer)
	 */
	@Override
	public Event getEvent(Integer eventId) {
		return (Event) sessionFactory.getCurrentSession().get(Event.class, eventId);
	}
	
	/**
	 * @see org.openmrs.module.datalogging.db.DataLoggingDAO#getEventsByEvent(EventType)
	 */
	@SuppressWarnings("unchecked")
	@Override
	public List<Event> getEventsByEvent(EventType eventType) {
		Criteria criteria = sessionFactory.getCurrentSession().createCriteria(Event.class);
		criteria.add(Restrictions.eq("eventType", eventType));
		
		criteria.add(Restrictions.eq("retired", false));
		criteria.addOrder(Order.desc("dateCreated"));
		criteria.addOrder(Order.desc("dateChanged"));
		return criteria.list();
	}
	
	/**
	 * @see org.openmrs.module.datalogging.db.DataLoggingDAO#getAllEvents()
	 */
	@SuppressWarnings("unchecked")
	@Override
	public List<Event> getAllEvents() {
		Criteria criteria = sessionFactory.getCurrentSession().createCriteria(Event.class);
		criteria.add(Restrictions.eq("retired", false));
		criteria.addOrder(Order.desc("dateCreated"));
		criteria.addOrder(Order.desc("dateChanged"));
		
		return criteria.list();
	}
	
	/**
	 * @see org.openmrs.module.datalogging.db.DataLoggingDAO#findEvents(java.lang.String)
	 */
	@SuppressWarnings("unchecked")
	@Override
	public List<Event> findEvents(String partialName) {
		Criteria criteria = sessionFactory.getCurrentSession().createCriteria(Event.class);
		
		criteria.add(Restrictions.ilike("name", "%" + partialName + "%"));
		criteria.add(Restrictions.eq("retired", false));
		criteria.addOrder(Order.desc("dateCreated"));
		criteria.addOrder(Order.desc("dateChanged"));
		
		return criteria.list();
	}
	
	/**
	 * @see org.openmrs.module.datalogging.db.DataLoggingDAO#getEvent(java.lang.String)
	 */
	@Override
	public Event getEvent(String name) {
		Criteria criteria = sessionFactory.getCurrentSession().createCriteria(Event.class);
		
		criteria.add(Restrictions.eq("name", name));
		criteria.add(Restrictions.eq("retired", false));
		
		return (Event) criteria.uniqueResult();
	}
	
	/**
	 * @see org.openmrs.module.datalogging.db.DataLoggingDAO#saveComponent(org.openmrs.module.datalogging.configuration.Variable)
	 */
	@Override
	public Variable saveComponent(Variable variable) {
		sessionFactory.getCurrentSession().saveOrUpdate(variable);
		return variable;
	}
	
	/**
	 * @see org.openmrs.module.datalogging.db.DataLoggingDAO#getComponent(java.lang.Integer)
	 */
	@Override
	public Variable getComponent(Integer variableId) {
		return (Variable) sessionFactory.getCurrentSession().get(Variable.class, variableId);
	}
	
	/**
	 * @see org.openmrs.module.datalogging.db.DataLoggingDAO#getAllComponents()
	 */
	@SuppressWarnings("unchecked")
	@Override
	public List<Variable> getAllComponents() {
		Criteria criteria = sessionFactory.getCurrentSession().createCriteria(Variable.class);
		criteria.add(Restrictions.eq("retired", false));
		criteria.addOrder(Order.desc("dateCreated"));
		criteria.addOrder(Order.desc("dateChanged"));
		return criteria.list();
	}
	
	/**
	 * @see org.openmrs.module.datalogging.db.DataLoggingDAO#findComponents(java.lang.String)
	 */
	@SuppressWarnings("unchecked")
	@Override
	public List<Variable> findComponents(String partialName) {
		Criteria criteria = sessionFactory.getCurrentSession().createCriteria(Variable.class);
		
		criteria.add(Restrictions.ilike("name", "%" + partialName + "%"));
		criteria.add(Restrictions.eq("retired", false));
		criteria.addOrder(Order.desc("dateCreated"));
		criteria.addOrder(Order.desc("dateChanged"));
		
		return criteria.list();
	}
	
	/**
	 * @see org.openmrs.module.datalogging.db.DataLoggingDAO#getComponent(java.lang.String)
	 */
	@Override
	public Variable getComponent(String name) {
		Criteria criteria = sessionFactory.getCurrentSession().createCriteria(Variable.class);
		
		criteria.add(Restrictions.eq("name", name));
		criteria.add(Restrictions.eq("retired", false));
		
		return (Variable) criteria.uniqueResult();
	}
	
	/**
	 * @see org.openmrs.module.datalogging.db.DataLoggingDAO#findEncounters(org.openmrs.Person,
	 *      java.util.List, java.util.List)
	 */
	@SuppressWarnings("unchecked")
	@Override
	public List<Encounter> findEncounters(Person person, Map<String, Set<BaseOpenmrsObject>> map) {
		Criteria criteria = sessionFactory.getCurrentSession().createCriteria(Encounter.class);
		
		criteria.createAlias("patient", "patient");
		criteria.add(Restrictions.eq("patient.patientId", person.getPersonId()));
		
		for (String property : map.keySet())
	        criteria.add(Restrictions.in(property, map.get(property)));
		
		criteria.add(Restrictions.eq("voided", false));
		criteria.addOrder(Order.asc("encounterDatetime"));
		return criteria.list();
	}
	
	/**
	 * @see org.openmrs.module.datalogging.db.DataLoggingDAO#findObservations(org.openmrs.Person,
	 *      org.openmrs.Concept, java.util.List)
	 */
	@SuppressWarnings("unchecked")
	@Override
	public List<Obs> findObservations(Person person, Map<String, Set<BaseOpenmrsObject>> map) {
		Criteria criteria = sessionFactory.getCurrentSession().createCriteria(Obs.class);
		
		criteria.add(Restrictions.eq("person", person));
		
		for (String property : map.keySet())
	        criteria.add(Restrictions.in(property, map.get(property)));
		
		criteria.add(Restrictions.eq("voided", false));
		criteria.addOrder(Order.asc("obsDatetime"));
		
		return criteria.list();
	}

	/**
	 * @see org.openmrs.module.datalogging.db.DataLoggingDAO#countEvents(java.lang.String)
	 */
	@Override
    public Integer countEvents(String search) {
		Criteria criteria = sessionFactory.getCurrentSession().createCriteria(LogEvent.class);
		
		criteria.add(Restrictions.eq("voided", false));
		
		criteria.setProjection(Projections.rowCount());
	    return (Integer) criteria.uniqueResult();
    }

	/**
	 * @see org.openmrs.module.datalogging.db.DataLoggingDAO#findEvents(java.lang.String, int, int)
	 */
	@SuppressWarnings("unchecked")
    @Override
    public List<LogEvent> findEvents(String search, int start, int length) {
		Criteria criteria = sessionFactory.getCurrentSession().createCriteria(LogEvent.class);
		
		criteria.add(Restrictions.eq("voided", false));
		criteria.addOrder(Order.desc("dateCreated"));
		criteria.addOrder(Order.desc("dateChanged"));
		
		criteria.setFirstResult(start);
		criteria.setMaxResults(length);
	    return criteria.list();
    }
}
